package com.bitin.adorn.config.orm;

import com.alibaba.fastjson2.JSON;
import com.bitin.adorn.common.orm.domain.AbstractAuditableModel;
import com.bitin.adorn.common.plugin.Snowflake;
import jakarta.annotation.PostConstruct;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 雪花算法ID生成器
 *
 * @author Mr.Lu
 * @since 2024/12/6 11:53
 */
public class SnowflakeIdGenerator implements IdentifierGenerator {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 毫秒格式化时间
     */
    public static final DateTimeFormatter MILLISECOND = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");

    /**
     * 数据中心ID
     */
    @Value("${app.snowflake.datacenter-id:1}")
    private long datacenterId;
    /**
     * 机器ID
     */
    @Value("${app.snowflake.worker-id:0}")
    private long workerId;

    private Snowflake snowflake;

    @PostConstruct
    public void init() {
        this.snowflake = new Snowflake(workerId, datacenterId);
    }

    /**
     * 重写IdentifierGenerator的方法
     *
     * @param session
     * @param object
     * @return
     * @throws HibernateException
     */
    @Override
    public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
        System.err.println(JSON.toJSONString(object));
        AbstractAuditableModel model = (AbstractAuditableModel) object;
        if (StringUtils.isNotBlank(model.getId())) {
            return model.getId();
        }

        String id = formatMillisecond() + snowflake.nextIdStr();
        logger.error(String.format("雪花算法ID ：%s", id));
        return id;
    }

    private String formatMillisecond() {
        return LocalDateTime.now().format(MILLISECOND);
    }
}
